package com.mrzhou.log.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mrzhou.log.aspect.ApiLogAspect;
import com.mrzhou.log.aspect.CallLogAspect;
import com.mrzhou.log.constant.LogConstant;
import com.mrzhou.log.event.ApiLogListener;
import com.mrzhou.log.event.ErrorLogListener;
import com.mrzhou.log.feign.factory.LogFeignFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import javax.annotation.PostConstruct;

/**
 * 自定义日志启动配置类
 * @author qiang.zhou
 */
@Configuration
@Import(RabbitAutoConfiguration.class)
@EnableFeignClients(basePackages = {"com.mrzhou.log"})
public class CloudLogConfiguration {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ObjectMapper objectMapper;

    @Bean
    public ApiLogAspect apiLogAspect() {
        return new ApiLogAspect();
    }

    /**
     * 设置为可选配置
     */
    @Bean
    @ConditionalOnProperty(prefix = "cloud.log.request", name = "enable", havingValue = "true", matchIfMissing = true)
    public CallLogAspect callLogAspect() {
        return new CallLogAspect();
    }

    /**========== 日志组件中需要交给spring管理的Bean Start ==========*/
    @Bean
    public ApiLogListener apiLogListener() {
        return new ApiLogListener();
    }

    @Bean
    public ErrorLogListener errorLogListener() {
        return new ErrorLogListener();
    }

    @Bean
    public LogFeignFactory logFeignFactory() {
        return new LogFeignFactory();
    }
    /**========== 日志组件中需要交给spring管理的Bean Start ==========*/

    /**========== RabbitMQ 初始化配置 Start ==========*/
    /**
     * {@link PostConstruct} 当类的Bean注入完成后自动执行方法中的代码，类似于static静态代码块;
     * 被注解修饰的方法执行顺序：
     *  <p>Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)<p/>
     */
    @PostConstruct
    public void rabbitTemplate() {
        // 定义mq消息序列化及反序列化
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter(objectMapper));
    }

    /**
     * 配置RabbitMQ消费者的MessageConverter为Jackson2
     * 此配置目测会覆盖application.yml中的配置值
     * @param connectionFactory
     * @return
     */
    @Bean
    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        // 将RabbitMQ自动确认改为手动确认，此配置会覆盖application.yml的配置项
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        factory.setMessageConverter(new Jackson2JsonMessageConverter(objectMapper));
        return factory;
    }

    /**
     * 日志记录的队列 {https://blog.csdn.net/fsgsggd/article/details/81349553}
     * <p>{@link Queue}构造函数的三个boolean含义</p>
     *  <p>durable：是否将队列的数据持久化(写入到磁盘)<p/>
     *  <p>exclusive：是否排外的, 为true时队列加锁,一次只有一个消费者处理<p/>
     *  <p>autoDelete：当队列没有任何消费者时是否自动删除队列<p/>
     */
    @Bean
    public Queue createQueue() {
        return new Queue(LogConstant.LOG_QUEUE, true, false, false);
    }

    @Bean
    public TopicExchange createExchange() {
        return new TopicExchange(LogConstant.LOG_EXCHANGE);
    }

    @Bean
    public Binding createBinding() {
        return BindingBuilder
                // 队列
                .bind(createQueue())
                // 将队列绑定到交换器
                .to(createExchange())
                // 交换器和队列绑定的规则(路由键)
                .with(LogConstant.LOG_QUEUE);
    }
    /**========== RabbitMQ 初始化配置 Start ==========*/



}
